<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    <title>HELLDIVERS Practice</title>
    <link href="./img/helldiver.ico" rel="icon">
    <!-- thanks for icon from https://helldivers-ii.fandom.com/wiki/Helldivers_II_Wiki -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.4.21/vue.global.prod.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/vuetify/3.5.9/vuetify.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vuetify/3.5.9/vuetify.min.js"></script>
    <script src="./js/balls.js"></script>
    <script src="./js/sounds.js"></script>
    <link rel="stylesheet" href="./css/index.css">

    <!-- Google tag (gtag.js) -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=G-31GM7RWSBJ"></script>
    <script>
        window.dataLayer = window.dataLayer || [];
        function gtag() { dataLayer.push(arguments); }
        gtag('js', new Date());

        gtag('config', 'G-31GM7RWSBJ');
    </script>
</head>

<body>
    <div id="app">
        <div id="banlist">
            <div v-for="ballClass in getBallClasses" v-bind="{id:ballClass}">
                <v-tooltip text="全选/全不选">
                    <template v-slot:activator="{ props }">
                        <v-btn :ripple="{ color: 'hsl(0, 0%, 40%)' }" v-bind="props"
                            @click="switchAvailableClass(ballClass)">{{ballClass}}</v-btn></template>
                </v-tooltip>
                <div v-bind="{class:'balls'}">
                    <v-btn :ripple="{ color: 'hsl(0, 0%, 40%)' }" color="hsl(0, 0%, 5%)"
                        v-for="ball in ballFilterByClass(ballClass)"
                        v-bind="{name:ball.name,class:'ball',available:ball.available}"
                        @click="switchAvailableBalls($event)">
                        <v-img :aspect-ratio="1/1" cover v-bind="{src:ball.icon,class:'ball-icon'}" v-on:error="ball.icon='./img/no-resource.svg'">
                        </v-img>
                        <v-tooltip activator="parent" :open-on-click="true" :open-on-hover="true"
                            location="end">{{ball.name}}
                            <span v-bind="{class:'time_avg'}" v-if="ball.time != undefined">{{avg(ball.time)}}s</span>
                            <div class="commandsplay"><span v-for="command in Array.from(ball.commands)"
                                    v-bind="{command:command}">{{command}}</span></div>
                        </v-tooltip>
                    </v-btn>
                </div>
            </div>
        </div>

        <div id="playground">
            <div id="ballplay">
                <div v-for="ball in playBalls" v-bind="{class:'ball-container',playing:ball.playing}">
                    <div v-bind="{class:'timer'}" v-if="ball.time_end != null">{{(ball.time_end -
                        ball.time_start).toFixed(2)}}s</div>
                    <v-img :aspect-ratio="1/1" cover v-bind="{src:ball.icon,class:'ball-icon'}">
                    </v-img>
                </div>
            </div>
            <div id="playingball" v-if="playBalls[0] != undefined">{{playBalls.filter(ball => ball.playing ==
                true).length == 0?'完成': playBalls.filter(ball => ball.playing == true)[0].name}}
            </div>
            <div class="commandsplay">
                <span v-for="command in playingCommands"
                    v-bind="{command:command.command,finished:command.finished}">{{command.command}}</span>
            </div>
            <div v-bind="{id:'kps'}" v-if=" playBalls[0] != null && playingCommands.slice(-1)[0].finished == true">
                {{kps.toFixed(2)}} key/s</div>
        </div>

        <div id="simpad" class="commandsplay">
            <div class="option" display="false">
                <v-btn @click="playSound('button_blank')">
                    <div class="switch-opa">
                        <v-img :aspect-ratio="1/1" cover v-bind="{src:'./img/gear-solid.svg'}">
                        </v-img>
                    </div>
                    <v-menu :close-on-content-click="false" location="top" activator="parent">
                        <div id="config-container">
                            <v-tooltip activator="parent" location="top">选择设置项后，按下要设置的按键</v-tooltip>
                            <v-text-field label="Start" v-model="inputSet.Start"
                                @keydown="setInput($event)"></v-text-field>
                            <v-text-field label="Up" v-model="inputSet.Up" @keydown="setInput($event)"></v-text-field>
                            <div class="mute-options">
                                <v-btn v-bind="{class:'sound',mute:muteAll}"
                                    @click="function() {muteAll = muteAll? false:true}">
                                    <div class="mute-option"></div>
                                </v-btn>
                                <v-btn v-bind="{class:'music',mute:muteMusic}"
                                    @click="function() {muteMusic = muteMusic? false:true}">
                                    <div class="mute-option"></div>
                                </v-btn>
                            </div>
                            <v-text-field label="Left" v-model="inputSet.Left"
                                @keydown="setInput($event)"></v-text-field>
                            <v-text-field label="Down" v-model="inputSet.Down"
                                @keydown="setInput($event)"></v-text-field>
                            <v-text-field label="Right" v-model="inputSet.Right"
                                @keydown="setInput($event)"></v-text-field>
                        </div>
                    </v-menu>
                </v-btn>
            </div>
            <div class="start-key">
                <button v-bind="{class:'key-container'}" @click="simpadClick($event,inputSet.Start)">
                    <span command="start">start</span></button>
            </div>
            <div class="key-play">
                <button v-bind="{class:'key-container'}">
                    <span command=""></span></button>
                <button v-bind="{class:'key-container'}" @click="simpadClick($event,'ArrowUp')">
                    <span command="u">u</span></button>
                <button v-bind="{class:'key-container'}">
                    <span command=""></span></button>
                <button v-bind="{class:'key-container'}" @click="simpadClick($event,'ArrowLeft')">
                    <span command="l">l</span></button>
                <button v-bind="{class:'key-container'}" @click="simpadClick($event,'ArrowDown')">
                    <span command="d">d</span></button>
                <button v-bind="{class:'key-container'}" @click="simpadClick($event,'ArrowRight')">
                    <span command="r">r</span></button>
            </div>
        </div>
    </div>
</body>

<script>
    const { createApp, computed, onMounted } = Vue
    const { createVuetify } = Vuetify

    const vuetify = createVuetify()

    const app = createApp({
        data() {
            return {
                balls: JSON.parse(JSON.stringify(balls_colle)), //fuck
                playBalls: [],
                playingCommands: [],
                inputSet: { Up: "KeyW", Left: "KeyA", Down: "KeyS", Right: "KeyD", Start: "Enter" },
                sounds: sounds_colle,
                bcPlayedTimes: 0,
                muteAll: false,
                muteMusic: false
            }
        },
        computed: {
            getBallClasses() {
                let classes = []
                Array.from(this.balls).forEach(ball => {
                    if (!classes.includes(ball.class))
                        classes.push(ball.class)
                })
                return classes
            },
            ballFilterByClass() {
                return function (filter_name) {
                    return this.balls.filter(ball => ball.class == filter_name)
                }
            },
            kps() {
                let time_totol = 0, command_totol = 0
                this.playBalls.forEach(ball => {
                    time_totol += ball.time_end - ball.time_start
                    command_totol += ball.commands.length
                })
                return command_totol / time_totol
            }
        },
        methods: {
            switchAvailableBalls(event) {
                this.playSound("button_blank")
                this.balls.filter(ball => ball.name == event.target.parentNode.parentNode.parentNode.getAttribute("name")).forEach(ball => {
                    ball.available = ball.available == true ? false : true
                })
            },
            switchAvailableClass(className) {
                this.playSound("button_blank")
                let classBalls = this.ballFilterByClass(className)
                if (classBalls.filter(ball => ball.available == false)[0] == undefined)
                    classBalls.forEach(ball => { ball.available = false })
                else
                    classBalls.forEach(ball => { ball.available = true })
            },
            setPlayingCommands(ball) {
                if (ball == null)
                    return
                let commands = Array.from(ball.commands)
                commands.forEach((command, index) => {
                    commandObj = { command: command, finished: false }
                    commands[index] = commandObj
                })
                this.playingCommands = commands
            },
            setPlayBalls() {
                let availableBalls = Array.from(this.balls.filter(ball => ball.available == true)) // why is this deep clone
                if (availableBalls.length == 0) {
                    return
                }
                this.playSound("start")
                availableBalls.forEach(ball => {
                    ball.playing = false

                    ball.time_start = null  //for timer
                    ball.time_end = null

                    let times = [2]
                    times.forEach(time => {
                        if (Math.random() * time > 1) {
                            let ballcopy = Object.assign({}, ball) //深拷贝
                            availableBalls.push(ballcopy)
                        }
                    })
                })
                availableBalls.forEach((ball, index) => {
                    if (Math.random() > 0.5) {
                        let swapball = availableBalls[index]
                        availableBalls[index] = availableBalls[0]
                        availableBalls[0] = swapball
                    }
                })
                let playballs = availableBalls.filter(ball => Math.random() > 0.5).slice(0, Math.random() * 5 + 1 + 3)
                if (playballs.length == 0)
                    playballs[0] = availableBalls[0]
                playballs[0].playing = true
                this.setPlayingCommands(playballs[0])
                this.playBalls = playballs
            },
            commandInput(event) {
                if (event.target != undefined && event.target.tagName == "INPUT")
                    return
                let input = ''
                switch (event.code) {
                    case 'ArrowUp': input = 'u'; break;
                    case 'ArrowRight': input = 'r'; break;
                    case 'ArrowDown': input = 'd'; break;
                    case 'ArrowLeft': input = 'l'; break;
                    case this.inputSet.Up: input = 'u'; break;
                    case this.inputSet.Right: input = 'r'; break;
                    case this.inputSet.Down: input = 'd'; break;
                    case this.inputSet.Left: input = 'l'; break;
                    case this.inputSet.Start: this.setPlayBalls(); input = "s"; break;
                    default: this.playSound("button_blank"); return;
                }

                if (this.playBalls.filter(ball => ball.playing == true)[0] != undefined) {
                    if (this.playBalls[0].playing == true && this.playingCommands[0].finished == false) //timer for only first ball
                        this.playBalls.filter(ball => ball.playing == true)[0].time_start = (new Date()).getTime() / 1000.0

                    let playingcommand = this.playingCommands.filter(command => command.finished == false)[0]

                    if (input == playingcommand.command) {
                        this.playSound("button")
                        playingcommand.finished = true
                    }
                    else if (input != "s") {
                        this.playSound("wrong")
                        this.playingCommands.forEach(command => {
                            command.finished = false
                        })
                    }
                    if (this.playingCommands.filter(command => command.finished == false).length == 0) {

                        this.playBalls.filter(ball => ball.playing == true)[0].time_end = (new Date()).getTime() / 1000.0 //timer

                        for (let index = 0; index < this.playBalls.length - 1; index = index + 1) {
                            if (this.playBalls[index].playing == true) {
                                this.playSound("ballCompleted")
                                this.playBalls[index].playing = false
                                this.playBalls[index + 1].playing = true
                                this.playBalls[index + 1].time_start = (new Date()).getTime() / 1000.0
                                this.setPlayingCommands(this.playBalls[index + 1])
                                break
                            }
                        }
                    }
                }

                if (this.playBalls.slice(-1)[0] != undefined && this.playBalls.slice(-1)[0].playing == true && this.playingCommands.slice(-1)[0].finished == true) { //input when game finishes

                    //randomize end sound effect
                    switch (this.bcPlayedTimes % 3) {
                        case 0: this.playSound("balls_completed_type1"); break;
                        case 1: this.playSound("balls_completed_type2"); break;
                        case 2: this.playSound("balls_completed_type3"); break;
                    }
                    this.bcPlayedTimes += 1

                    this.playBalls.forEach(playBall => {
                        if (playBall.time == undefined)
                            playBall.time = []
                        playBall.time.push(playBall.time_end - playBall.time_start)
                        this.balls.filter(ball => ball.name == playBall.name)[0].time = playBall.time
                    })
                    this.playBalls.slice(-1)[0].playing = false
                    return
                }

                if (this.playingCommands.filter(command => command.finished == false)[0] == undefined) {
                    switch (event.code) {
                        case 'ArrowUp':
                        case 'ArrowRight':
                        case 'ArrowDown':
                        case 'ArrowLeft':
                        case this.inputSet.Up:
                        case this.inputSet.Right:
                        case this.inputSet.Down:
                        case this.inputSet.Left: {
                            this.playSound("button")
                            return
                        }
                        default: {
                            this.playSound("button_blank")
                            return
                        }
                    }
                }
            },
            simpadClick(event, key) {
                event.preventDefault()
                this.commandInput({ code: key })
            },
            setInput(event) {
                event.preventDefault()
                this.inputSet[event.target.parentNode.childNodes[0].innerText] = event.code
            },
            avg(arr) {
                return (arr.reduce((ante, post) => {
                    return ante + post
                }) / arr.length).toFixed(2)
            },
            playSound(event) {
                if (!this.muteAll) {
                    let soundNow = this.sounds.filter(sound => sound.name == event && sound.playing == false)[0] //one dom can't play twice at a time
                    if (soundNow == undefined) return
                    soundNow.dom.volume = soundNow.volume
                    soundNow.playing = true
                    soundNow.dom.play()
                    setTimeout(function () { soundNow.playing = false }, soundNow.time + 100)
                }
            }
        },
        mounted() {
            window.addEventListener('keydown', this.commandInput)
            this.balls.forEach(ball => {
                ball.available = false
            })
        }
    })
    app.use(vuetify).mount('#app')
</script>

</html>